﻿using System.Text.Json;
using System.Text.RegularExpressions;
using BP.Plugin.Monitor.Application.Common.Interfaces;
using BP.Plugin.Monitor.Domain.Entities;
using Finbuckle.MultiTenant;
using Finbuckle.MultiTenant.Abstractions;
using Infrastructure.Tenant;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace BP.Plugin.Monitor.Infrastructure.Middlewares;

public class LoginLogMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IMonitorDbContext _db;
    private readonly ILogger<LoginLogMiddleware> _logger;
    private readonly IMultiTenantStore<BoilerplateTenantInfo> _tenantStore;
    private readonly IServiceScopeFactory _scopeFactory;

    public LoginLogMiddleware(
        RequestDelegate next,
        IMonitorDbContext db,
        ILogger<LoginLogMiddleware> logger,
        IMultiTenantStore<BoilerplateTenantInfo> tenantStore,
        IServiceScopeFactory scopeFactory)
    {
        _next = next;
        _db = db;
        _logger = logger;
        _tenantStore = tenantStore;
        _scopeFactory = scopeFactory;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.Request.Path.StartsWithSegments("/api/token/generate", StringComparison.OrdinalIgnoreCase))
        {
            var loginTime = DateTime.UtcNow;
            string username = "未知";
            string ip = context.Connection.RemoteIpAddress?.ToString() ?? "未知";
            string ua = context.Request.Headers["User-Agent"].ToString();
            string system = DetectOS(ua);
            string browser = DetectBrowser(ua);
            string address = await GetAddressAsync(ip);
            int status = 200;
            string behavior = "账号登录";
            string failReason = string.Empty;

            // 复制原始请求体以备后续使用
            var requestBodyStream = new MemoryStream();
            var originalRequestBody = context.Request.Body;
            await originalRequestBody.CopyToAsync(requestBodyStream);
            requestBodyStream.Seek(0, SeekOrigin.Begin);
            var requestBodyText = await new StreamReader(requestBodyStream).ReadToEndAsync();
            context.Request.Body = originalRequestBody; // 将请求体重置为原始状态
            string? requestBody = requestBodyText;

            string pattern = "\"username\"\\s*:\\s*\"([^\"]+)\"";
            Match match = Regex.Match(requestBody, pattern);
            if (match.Success)
            {
                username = match.Groups[1].Value;
            }

            // 重置请求体
            requestBodyStream.Seek(0, SeekOrigin.Begin);
            context.Request.Body = requestBodyStream;

            // 处理请求
            using (var responseBodyStream = new MemoryStream())
            {
                var originalResponseBody = context.Response.Body;
                context.Response.Body = responseBodyStream;

                try
                {
                    await _next(context);

                    Endpoint? endpoint = context.GetEndpoint();


                    responseBodyStream.Seek(0, SeekOrigin.Begin);
                    var responseBodyText = await new StreamReader(responseBodyStream).ReadToEndAsync();
                   
                    responseBodyStream.Seek(0, SeekOrigin.Begin);
                    await responseBodyStream.CopyToAsync(originalResponseBody);
                }
                catch (Exception ex)
                {
                    status = 500;
                    failReason = ex.Message;
                }
                finally
                {
                    context.Response.Body = originalResponseBody;

                    _logger.LogInformation(
                   "【登录日志】用户:{UserName}, IP:{IP}, 地址:{Address}, 系统:{System}, 浏览器:{Browser}, 状态:{Status}, 时间:{Time}, 行为:{Behavior}, 失败原因:{FailReason}",
                   username,
                   ip,
                   address,
                   system,
                   browser,
                   status,
                   loginTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"),
                   behavior,
                   failReason);

                   try
                   {
                        var tenantId = context.Request.Headers["tenant"].ToString();
                        var tenant = await _tenantStore.TryGetAsync(tenantId);
                        if (tenant != null)
                        {
                            using var scope = _scopeFactory.CreateScope();
                            scope.ServiceProvider.GetRequiredService<IMultiTenantContextSetter>()
                                .MultiTenantContext = new MultiTenantContext<BoilerplateTenantInfo>()
                                {
                                    TenantInfo = tenant
                                };

                            var db = scope.ServiceProvider.GetRequiredService<IMonitorDbContext>();

                            var user = await db.OnlineUsers.FirstOrDefaultAsync(x => x.UserName == username && x.IsOnline == true);
                            if (user != null)
                            {
                                user.IsOnline = false;
                                user.LastActiveTime = DateTime.UtcNow;
                                db.OnlineUsers.Update(user);
                            }
                            var now = DateTime.UtcNow;
                            await db.OnlineUsers.AddAsync(new OnlineUser
                            {
                                UserName = username,
                                IP = ip,
                                Browser = browser,
                                System = system,
                                Address = address,
                                LoginTime = now,
                                LastActiveTime = now,
                                IsOnline = true,
                                Status = status,
                                Behavior = behavior,
                                Description = failReason
                            });

                            await db.SaveChangesAsync();
                        }          
                   }
                   catch (Exception)
                   {
                   }
                }
            }
        }
        else
        {
            await _next(context);
        }
    }

    private static string DetectOS(string ua)
    {
        if (ua.Contains("Windows")) return "Windows";
        if (ua.Contains("Mac OS")) return "MacOS";
        if (ua.Contains("Linux")) return "Linux";
        if (ua.Contains("Android")) return "Android";
        if (ua.Contains("iPhone")) return "iOS";
        return "未知";
    }

    private static string DetectBrowser(string ua)
    {
        if (ua.Contains("Chrome")) return "Chrome";
        if (ua.Contains("Edge")) return "Edge";
        if (ua.Contains("Firefox")) return "Firefox";
        if (ua.Contains("Safari")) return "Safari";
        return "未知";
    }

    private static async Task<string> GetAddressAsync(string ip)
    {
        if (string.IsNullOrWhiteSpace(ip) || ip == "::1") return "本地";
        try
        {
            using var client = new HttpClient();
            var res = await client.GetStringAsync($"https://ipapi.co/{ip}/json/");
            using var json = JsonDocument.Parse(res);
            var city = json.RootElement.GetProperty("city").GetString();
            var country = json.RootElement.GetProperty("country_name").GetString();
            return $"{country}·{city}";
        }
        catch
        {
            return "未知";
        }
    }
}
